﻿using System;
using System.Diagnostics.Contracts;
using System.Net;
using System.Text;

namespace Server
{
    /// <summary>
    /// Server parameters.
    /// </summary>
    public class ServerParameters
    {
        #region Fields

        /// <summary>
        /// The port on which to listen for incoming connection attempts.
        /// </summary>
        /// <remarks>Port must be between <see cref="F:System.Net.IPEndPoint.MinPort" /> and <see cref="F:System.Net.IPEndPoint.MaxPort" />.</remarks>
        private int m_connectionPort = Constants.ConnectionPortDefaultValue;

        /// <summary>
        /// The current version of the client software.
        /// </summary>
        private string m_clientVersion = Constants.ClientVersionDefaultValue;

        /// <summary>
        /// The address to use in order to update the client software..
        /// </summary>
        private string m_updateAddress = Constants.UpdateAddressDefaultValue;

        /// <summary>
        /// The port on which to listen for client communication attempts.
        /// </summary>
        /// <remarks>Port must be between <see cref="F:System.Net.IPEndPoint.MinPort" /> and <see cref="F:System.Net.IPEndPoint.MaxPort" />.</remarks>
        private int m_serverPort = Constants.ServerPortDefaultValue;

        /// <summary>
        /// The maximum length of the pending connections queue.
        /// </summary>
        private int m_pendingConnectionsQueueLength = Constants.PendingConnectionsQueueLengthDefaultValue;

        /// <summary>
        /// The number of milliseconds during which the <see cref="T:Server.TcpServer" /> will be sleeping when it has no work to do.
        /// </summary>
        private int m_serverIdleTime = Constants.ServerIdleTimeDefaultValue;

        /// <summary>
        /// How often (in milliseconds) the <see cref="T:Server.TcpServer" /> should verify that a client is still connected.
        /// </summary>
        private int m_verifyConnectionInterval = Constants.VerifyConnectionIntervalDefaultValue;

        /// <summary>
        /// The number of milliseconds to wait before the connection with a client is considered to be lost.
        /// </summary>
        private int m_clientTimeout = Constants.ClientTimeoutDefaultValue;

        /// <summary>
        /// The number of milliseconds during which the <see cref="T:Server.TcpClientManager" /> will be sleeping when it has no work to do.
        /// </summary>
        private int m_clientManagerIdleTime = Constants.ClientManagerIdleTimeDefaultValue;

        /// <summary>
        /// The character encoding method.
        /// </summary>
        private Encoding m_encoding = Constants.EncodingDefaultValue;

        #endregion

        #region Properties

        #region ConnectionPort

        /// <summary>
        /// The port on which to listen for incoming connection attempts.
        /// </summary>
        /// <remarks>Port must be between <see cref="F:System.Net.IPEndPoint.MinPort" /> and <see cref="F:System.Net.IPEndPoint.MaxPort" />.</remarks>
        public int ConnectionPort
        {
            get
            {
                Contract.Ensures(Contract.Result<int>() >= IPEndPoint.MinPort && Contract.Result<int>() <= IPEndPoint.MaxPort);

                return m_connectionPort;
            }
            set
            {
                Contract.Requires<ArgumentOutOfRangeException>(value >= IPEndPoint.MinPort && value <= IPEndPoint.MaxPort);

                m_connectionPort = value;
            }
        }

        #endregion

        #region ClientVersion

        /// <summary>
        /// The current version of the client software.
        /// </summary>
        public string ClientVersion
        {
            get
            {
                Contract.Ensures(!String.IsNullOrWhiteSpace(Contract.Result<string>()));

                return m_clientVersion;
            }
            set
            {
                Contract.Requires<ArgumentNullException>(!String.IsNullOrWhiteSpace(value));

                m_clientVersion = value;
            }
        }

        #endregion

        #region UpdateAddress

        /// <summary>
        /// The address to use in order to update the client software.
        /// </summary>
        public string UpdateAddress
        {
            get
            {
                Contract.Ensures(!String.IsNullOrWhiteSpace(Contract.Result<string>()));

                return m_updateAddress;
            }
            set
            {
                Contract.Requires<ArgumentNullException>(!String.IsNullOrWhiteSpace(value));

                m_updateAddress = value;
            }
        }

        #endregion

        #region ServerPort

        /// <summary>
        /// The port on which to listen for client communication attempts.
        /// </summary>
        /// <remarks>Port must be between <see cref="F:System.Net.IPEndPoint.MinPort" /> and <see cref="F:System.Net.IPEndPoint.MaxPort" />.</remarks>
        public int ServerPort
        {
            get
            {
                Contract.Ensures(Contract.Result<int>() >= IPEndPoint.MinPort && Contract.Result<int>() <= IPEndPoint.MaxPort);

                return m_serverPort;
            }
            set
            {
                Contract.Requires<ArgumentOutOfRangeException>(value >= IPEndPoint.MinPort && value <= IPEndPoint.MaxPort);

                m_serverPort = value;
            }
        }

        #endregion

        #region PendingConnectionsQueueLength

        /// <summary>
        /// The maximum length of the pending connections queue.
        /// </summary>
        public int PendingConnectionsQueueLength
        {
            get
            {
                Contract.Ensures(Contract.Result<int>() >= 0);

                return m_pendingConnectionsQueueLength;
            }
            set
            {
                Contract.Requires<ArgumentOutOfRangeException>(value >= 0);

                m_pendingConnectionsQueueLength = value;
            }
        }

        #endregion

        #region ServerIdleTime

        /// <summary>
        /// The number of milliseconds during which the <see cref="T:Server.TcpServer" /> will be sleeping when it has no work to do.
        /// </summary>
        public int ServerIdleTime
        {
            get
            {
                Contract.Ensures(Contract.Result<int>() >= 0);

                return m_serverIdleTime;
            }
            set
            {
                Contract.Requires<ArgumentOutOfRangeException>(value >= 0);

                m_serverIdleTime = value;
            }
        }

        #endregion

        #region VerifyConnectionInterval

        /// <summary>
        /// How often (in milliseconds) the <see cref="T:Server.TcpServer" /> should verify that a client is still connected.
        /// </summary>
        public int VerifyConnectionInterval
        {
            get
            {
                Contract.Ensures(Contract.Result<int>() >= 0);

                return m_verifyConnectionInterval;
            }
            set
            {
                Contract.Requires<ArgumentOutOfRangeException>(value >= 0);

                m_verifyConnectionInterval = value;
            }
        }

        #endregion

        #region ClientTimeout

        /// <summary>
        /// The number of milliseconds to wait before the connection with a client is considered to be lost.
        /// </summary>
        public int ClientTimeout
        {
            get
            {
                Contract.Ensures(Contract.Result<int>() >= 0);

                return m_clientTimeout;
            }
            set
            {
                Contract.Requires<ArgumentOutOfRangeException>(value >= 0);

                m_clientTimeout = value;
            }
        }

        #endregion

        #region ClientManagerIdleTime

        /// <summary>
        /// The number of milliseconds during which the <see cref="T:Server.TcpClientManager" /> will be sleeping when it has no work to do.
        /// </summary>
        public int ClientManagerIdleTime
        {
            get
            {
                Contract.Ensures(Contract.Result<int>() >= 0);

                return m_clientManagerIdleTime;
            }
            set
            {
                Contract.Requires<ArgumentOutOfRangeException>(value >= 0);

                m_clientManagerIdleTime = value;
            }
        }

        /// <summary>
        /// The character encoding method.
        /// </summary>
        public Encoding Encoding
        {
            get
            {
                Contract.Ensures(Contract.Result<Encoding>() != null);

                return m_encoding;
            }
            set
            {
                Contract.Requires<ArgumentNullException>(value != null);

                m_encoding = value;
            }
        }

        #endregion

        #endregion

        #region Methods

        /// <summary>
        /// Define the invariant valid state for this class.
        /// </summary>
        [ContractInvariantMethod]
        private void InvariantValidState()
        {
            Contract.Invariant(m_connectionPort >= IPEndPoint.MinPort && m_connectionPort <= IPEndPoint.MaxPort);
            Contract.Invariant(!String.IsNullOrWhiteSpace(m_clientVersion));
            Contract.Invariant(!String.IsNullOrWhiteSpace(m_updateAddress));
            Contract.Invariant(m_serverPort >= IPEndPoint.MinPort && m_serverPort <= IPEndPoint.MaxPort);
            Contract.Invariant(m_pendingConnectionsQueueLength >= 0);
            Contract.Invariant(m_serverIdleTime >= 0);
            Contract.Invariant(m_verifyConnectionInterval >= 0);
            Contract.Invariant(m_clientTimeout >= 0);
            Contract.Invariant(m_clientManagerIdleTime >= 0);
            Contract.Invariant(m_encoding != null);
        }

        #endregion
    }
}
